﻿#include "FTPManager.h"

static size_t my_fwrite(void *buffer, size_t size, size_t nmemb,void *stream)
{
    struct FtpFile *out=(struct FtpFile *)stream;
//    qDebug()<<"filename1:"<<out->filename;

      if(out && !out->stream) {
        /* open file for writing */
        out->stream=fopen(out->filename, "wb");
         qDebug()<<"open:"<<out->filename;
        if(!out->stream){
        qDebug()<<"fail filename:"<<out->filename;
        return -1;
        }
        /* failure, can't open file to write */
      }
//      qDebug()<<"filename:"<<out->filename;
      return fwrite(buffer, size, nmemb, out->stream);

}

static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream)
{
  curl_off_t nread;
  /* in real-world cases, this would probably get this data differently
     as this fread() stuff is exactly what the library already would do
     by default internally */
  size_t retcode = fread(ptr, size, nmemb, (FILE*)stream);

  nread = (curl_off_t)retcode;

  qDebug()<<"*** We read "<<CURL_FORMAT_CURL_OFF_T<<" bytes from file"<<endl<<nread<<endl;
  return retcode;
}

FTPManager::FTPManager(const QString ip, const QString port, const QString username, const QString password)
{
    m_localFile = nullptr;
    m_remoteUrl = nullptr;
    m_userNamePassWord = nullptr;
    m_remoteUrlPort = nullptr;

    setRemoteUrl(ip);
    setRemoteUrlPort(port);
    setUserNamePassWord(username,password);
}

FTPManager::~FTPManager()
{

}

void FTPManager::setLocalFilePath(QString localFilePath)
{
//    m_localFile = typeConver::QStringToChararry(localFilePath);
////    qDebug()<<"set localFile:"<<m_localFile<<endl;

//    QFileInfo fileInfo(localFilePath);
//    if(fileInfo.isDir())
//    {
//        m_rootPath = fileInfo.absolutePath();
//    }
//    else if(fileInfo.isFile())
//    {
//        m_rootPath = fileInfo.absolutePath();
//        qDebug()<<m_rootPath<<endl;
//        m_fileInfoList->append(fileInfo);
//    }
}

void FTPManager::setRemoteUrl(QString remoteUrl)
{
    remoteUrl = "ftp://"+remoteUrl;
//    remoteUrl += "/test/nihao.txt";
    m_remoteUrl = typeConver::QStringToChararry(remoteUrl);
//    qDebug()<<"set remoteUrl:"<<remoteUrl<<endl;
}

void FTPManager::setRemoteUrlPort(QString remoteUrlPort)
{
    m_remoteUrlPort = typeConver::QStringToChararry(remoteUrlPort);
//    qDebug()<<"set remoteUrlPort:"<<remoteUrlPort<<endl;
}

void FTPManager::setUserNamePassWord(QString userName,QString userpassWord)
{
    QString userNamePsw = userName + ":" + userpassWord;
    m_userNamePassWord = typeConver::QStringToChararry(userNamePsw);
    //    qDebug()<<"set userNamePassWord:"<<userNamePsw<<endl;
}

void FTPManager::getFileList(FileInfoList fileInfoList)
{
    st_fileInfoList = fileInfoList;
}

FTPManager *FTPManager::self;
FTPManager *FTPManager::getInstance()
{
    //判断是否是第一次调用
    //假如不为NULL,会直接return
    if (self == NULL) {

        //防止多进程同时调用创建2个对象的问题
        //原理是因为是静态变量只会定义一次
        static QMutex mutex;

        //上锁  QMutexLocker会锁住mutex，当QMutexLocker 被释放的时候会自动解锁
        //locker是局部变量，所以getInstance（）函数结束之后会自动解锁
        QMutexLocker locker(&mutex);

        //在判断一次,防止其它进程抢先new过了
        if (self == NULL) {
            self = new FTPManager;
        }
    }

    return self; //返回指针
}


int FTPManager::on_startFTPUpload()
{
    if(st_fileInfoList.fileInfoList->isEmpty())//文件列表无文件
    {
        return -1;//传输列表为空
    }
    CURL *curl;
    CURLcode res;
    FILE *hd_src;

    struct stat file_info;
    curl_off_t fsize;

    /* get the file size of the local file */
    QList<QFileInfo>::iterator fileitem;
    for(fileitem = st_fileInfoList.fileInfoList->begin();fileitem != st_fileInfoList.fileInfoList->end();fileitem++)
    {
        char *transfilePath = typeConver::QStringToChararry(fileitem->filePath());
        QString dirRootPath = fileitem->filePath().remove(st_fileInfoList.localFileRootPath);

        QString remoteUrl = typeConver::ChararryToQString(m_remoteUrl) + "/" + st_fileInfoList.remoteFtpFileRootPath + dirRootPath;
        char *tempRemoteUrl = typeConver::QStringToChararry(remoteUrl);

        if(stat(transfilePath, &file_info)) {
            qDebug()<<"Couldn't open "<<fileitem->filePath()<<" : "<<strerror(errno)<<endl;
            curl_global_cleanup();
            return 1;//传输过程中出错，没有传输成功;
        }
        fsize = (curl_off_t)file_info.st_size;

        qDebug()<<"Local file size:"<<CURL_FORMAT_CURL_OFF_T<<"bytes."<<endl;

        /* get a FILE * of the same file */
        hd_src = fopen(transfilePath, "rb");

        /* In windows, this will init the winsock stuff */
        curl_global_init(CURL_GLOBAL_ALL);

        /* get a curl handle */
        curl = curl_easy_init();
        if(curl) {
            /* build a list of commands to pass to libcurl */

            /* we want to use our own read function */
            curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);

            /* enable uploading */
            curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

            /* set userID & password*/
//            qDebug()<<"before: userNamePassWord:"<<m_userNamePassWord<<endl;
            curl_easy_setopt(curl,CURLOPT_USERPWD, m_userNamePassWord);
//            curl_easy_setopt(curl,CURLOPT_USERPWD, "admin:admin");

            /* set port*/
//            qDebug()<<"before: remoteUrlPort:"<<m_remoteUrlPort<<endl;
            curl_easy_setopt(curl,CURLOPT_PORT,m_remoteUrlPort);
//            curl_easy_setopt(curl,CURLOPT_PORT,21);

            /* specify target */
//            qDebug()<<"before: remoteUrl:"<<tempRemoteUrl<<endl;
            curl_easy_setopt(curl, CURLOPT_URL, tempRemoteUrl);

            /*creat dirpath*/
            curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR_RETRY);

            /* now specify which file to upload */
            curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);

            /* Set the size of the file to upload (optional).  If you give a *_LARGE
               option you MUST make sure that the type of the passed-in argument is a
               curl_off_t. If you use CURLOPT_INFILESIZE (without _LARGE) you must
               make sure that to pass in a type 'long' argument. */
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,(curl_off_t)fsize);

            /* Now run off and do what you've been told! */
            res = curl_easy_perform(curl);
            /* Check for errors */
            if(res != CURLE_OK)
            {
                qDebug()<<"curl_easy_perform() failed: "<<curl_easy_strerror(res)<<endl;
                curl_easy_cleanup(curl);
                fclose(hd_src); /* close the local file */
                curl_global_cleanup();
                return 1;//传输过程中出错，没有传输成功;
            }
            else
                qDebug()<<"finnish upload : "<<transfilePath<<endl;
            emit evt_finishOneFileTransfer();
            /* always cleanup */
            curl_easy_cleanup(curl);
        }
        fclose(hd_src); /* close the local file */
        curl_global_cleanup();
    }
    return 0;
}

int FTPManager::on_startFTPDownLoad()
{

    if(st_fileInfoList.fileInfoList->isEmpty())//文件列表无文件
    {
        return -1;//传输列表为空
    }

    CURL *curl;
    CURLcode res;
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    QList<QFileInfo>::iterator fileitem;
    for(fileitem = st_fileInfoList.fileInfoList->begin();fileitem != st_fileInfoList.fileInfoList->end();fileitem++)
    {
        //测试，这里用的是本地目录的文件列表
        QString absolutePath = fileitem->absolutePath();

        QString dirRootPath = fileitem->filePath().remove("D:/FTP/Localuser/admin/hello/");

        absolutePath.remove("D:/FTP/Localuser/admin/hello");

        char *transfilePath = typeConver::QStringToChararry(typeConver::ChararryToQString(m_remoteUrl) + "/" + st_fileInfoList.remoteFtpFileRootPath + dirRootPath);
        QString localFilePath = st_fileInfoList.localFileRootPath + "/" +dirRootPath;
        char *tempLocalFilePath = typeConver::QStringToChararry(localFilePath);

        absolutePath =  st_fileInfoList.localFileRootPath + absolutePath;
        //正常情况下应该是
        /*QString dirRootPath = fileitem->filePath().remove(typeConver::ChararryToQString(m_remoteUrl) + st_fileInfoList.remoteFtpFileRootPath);
         * char *transfilePath = typeConver::QStringToChararry(typeConver::ChararryToQString(m_remoteUrl) + st_fileInfoList.remoteFtpFileRootPath + dirRootPath);
         *QString localFilePath = st_fileInfoList.localFileRootPath + dirRootPath;
         *char *tempLocalFilePath = typeConver::QStringToChararry(localFilePath);*/

        struct FtpFile ftpfile = {
            tempLocalFilePath, /* name to store the file as if successful */
            NULL
         };
        FileFunction::isDirExistAndMake(absolutePath);
        if(curl) {
            curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1);
            curl_easy_setopt(curl, CURLOPT_USERPWD, m_userNamePassWord);
            curl_easy_setopt(curl,CURLOPT_PORT,m_remoteUrlPort);
            curl_easy_setopt(curl, CURLOPT_URL, transfilePath);
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile);
            curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
            curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
            res = curl_easy_perform(curl);
            if(CURLE_OK != res) {
                qDebug()<<"curl_easy_perform() failed: "<<curl_easy_strerror(res)<<endl;
                fclose(ftpfile.stream);
                curl_easy_cleanup(curl);
                curl_global_cleanup();
                return 1;//传输过程中出错，没有传输成功;
            }
            else
                qDebug()<<"finnish download : "<<transfilePath<<endl;
            fclose(ftpfile.stream);
            emit evt_finishOneFileTransfer();
            /* always cleanup */
        }
    }
    curl_easy_cleanup(curl);
    curl_global_cleanup();
    return 0;
}
